package com.yc.mvc.web;

import com.google.gson.Gson;
import com.yc.mvc.uitl.Utils;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

@WebServlet("/")
public class MyDispatchServlet extends HttpServlet {

    // IOC
    private Set<Object> handlerSet = new HashSet<>();
    // 地址控制器映射集合
    private Map<String, ControllerInfo> handlerMapping = new HashMap<>();

    // 块: 实力块 => 无名构造函数
    {
        handlerSet.add(new HelloAction());
        handlerSet.add(new DemoController());
        handlerSet.add(new IndexDo());

        for (Object controller : handlerSet) {
            RequestMapping requestMapping = controller.getClass().getAnnotation(RequestMapping.class);
            String prefix = ""; // 地址前缀
            if (requestMapping != null) {
                prefix = requestMapping.value().length == 0
                        ? requestMapping.path()[0]
                        : requestMapping.value()[0];
            }
            // 统一添加 / 开头
            prefix = "/" + prefix;
            for (Method method : controller.getClass().getDeclaredMethods()) {
                // 构建地址
                String path = prefix;
                if(path.equals("/") == false){
                    path += "/";
                }
                requestMapping = method.getAnnotation(RequestMapping.class);
                if (requestMapping != null) {
                    path += requestMapping.value().length == 0
                            ? requestMapping.path()[0]
                            : requestMapping.value()[0];
                } else {
                    GetMapping getMapping = method.getAnnotation(GetMapping.class);
                    if (getMapping != null) {
                        path += getMapping.value().length == 0
                                ? getMapping.path()[0]
                                : getMapping.value()[0];
                    }
                }
                ControllerInfo ci = new ControllerInfo(controller, method);
                handlerMapping.put(path, ci);
            }
        }
        handlerMapping.forEach((k, v) -> {
            System.out.printf(" %s => %s \n", k, v.method);
        });
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 通过请求地址查找控制器
        // 1. 精确匹配
        final String servletPath = req.getServletPath();
        System.out.println("servletPath = " + servletPath);
        final ControllerInfo controllerInfo = handlerMapping.get(servletPath);
        if (controllerInfo == null) {
            // TODO => 判断静态资源
        } else {
            try {
                final Method method = controllerInfo.getMethod();
                final String[] parameterNames = Utils.getParameterNames(method);
                Object[] values = new Object[parameterNames.length];
                for (int i = 0; i < parameterNames.length; i++) {
                    String parameterName = parameterNames[i];
                    Object value = null;
                    // 判断参数类型
                    if(method.getParameterTypes()[i].isAssignableFrom(String.class) ||
                            // 判断 字符串 + 基本数据类型 + 包装器类型
                            method.getParameterTypes()[i].isPrimitive() ||
                            method.getParameterTypes()[i].isAssignableFrom(Integer.class) ||
                            method.getParameterTypes()[i].isAssignableFrom(Long.class) ||
                            method.getParameterTypes()[i].isAssignableFrom(Boolean.class)){
                        // 将字符串类型参数转为指定类型的方法参数
                        String svalue = req.getParameter(parameterName);
                        value = cast(svalue, method.getParameterTypes()[i]);
                    } else {
                        // 实体类型  示例: value = new User();
                        value = method.getParameterTypes()[i].newInstance();
                        // 将请求参数值设置到实体对象的属性中
                        setObjectField(value, req);
                    }
                    values[i] = value;
                }

                Object res = method.invoke(controllerInfo.controller, values);
                // json or 跳转
                // @RestController
                // @Contorller  +  @ResponseBody
                if (controllerInfo.controller.getClass().getAnnotation(RestController.class) != null
                        || controllerInfo.controller.getClass().getAnnotation(Controller.class) != null
                        && method.getAnnotation(ResponseBody.class) != null) {
                    // json
                    resp.setContentType("application/json;charset=utf-8");
                    final String s = new Gson().toJson(res);
                    resp.getWriter().println(s);
                } else {
                    // TODO 跳转
                }
            } catch (Exception e) {
                throw new RuntimeException("控制器方法执行错误!", e);
            }
        }
    }

    private void setObjectField(Object object, HttpServletRequest req) throws IllegalAccessException {
        for (Field field : object.getClass().getDeclaredFields()) {
            if(field.getType().isAssignableFrom(String.class) ||
                    // 判断 字符串 + 基本数据类型 + 包装器类型
                    field.getType().isPrimitive() ||
                    field.getType().isAssignableFrom(Integer.class) ||
                    field.getType().isAssignableFrom(Long.class) ||
                    field.getType().isAssignableFrom(Boolean.class)){
                final String svalue = req.getParameter(field.getName());
                Object value = cast(svalue, field.getType());
                field.setAccessible(true);
                field.set(object, value);
            } else {
                // TODO 数组, 集合, 其它实体类型 => 扩展
            }
        }
    }

    private Object cast(String svalue, Class<?> parameterType) {
        if (parameterType.isAssignableFrom(String.class)) {
            return svalue;
        }
        if (parameterType.isAssignableFrom(int.class)){
            return Integer.parseInt(svalue);
        }
        // TODO 扩展其它类型
        return svalue;
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }

    public static void main(String[] args) {
        MyDispatchServlet myDispatchServlet = new MyDispatchServlet();
    }
}

@Data
@AllArgsConstructor
class ControllerInfo {
    Object controller;
    Method method;
}

/**
 * 扩展实现
 * 1. 地址参数的映射
 * 2. 请求转发 + 响应重定向
 */